/*
 * Time.java is designed as a customization of DateTime.java to uniform the representation of time in 
 * BeautyRoxy. An example extension is that it provides a method to add n days to this Time and return
 * the new Time object. It also provides some static methods that faciliates the extraction of date time 
 * information from a time string.
 * 
 * Moreover, Convertion from Time to Date and Date to Time is also provided.
 */
// @author A0105514U
/** @author A0105514U */

package global;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;

public class Time extends Date{
	
	private final String MONDAY = "Mon";
	private final String TUESDAY = "Tue";
	private final String WEDNESDAY = "Wed";
	private final String THURSDAY = "Thur";
	private final String FRIDAY = "Fri";
	private final String SATURDAY = "Sat";
	private final String SUNDAY = "Sun";
	private final String DATE_TIME_SEPARATOR = "/";
	private final String ZERO_ADDON = "0";
	
	private final int MON_INT = 1;
	private final int TUE_INT = 2;
	private final int WED_INT = 3;
	private final int THUR_INT = 4;
	private final int FRI_INT = 5;
	private final int SAT_INT = 6;
	private final int SUN_INT = 7;
	
	private static final int YEAR_STRING_START = 0;
	private static final int YEAR_STRING_END = 4;
	private static final int MONTH_STRING_START = 4;
	private static final int MONTH_STRING_END = 6;
	private static final int DATE_STRING_START = 6;
	private static final int DATE_STRING_END = 8;
	private static final int HOUR_STRING_START = 8;
	private static final int HOUR_STRING_END = 10;
	private static final int MIN_STRING_START = 10;
	private static final int MIN_STRING_END = 12;
	private static final int TIME_STRING_LENGTH = 12;
	
	private static final int MAX_DAY_IN_LONG_MONTH = 31;
	private static final int MAX_DAY_IN_SHORT_MONTH = 20;
	private static final int MAX_DAY_IN_FEB_LEAP_YEAR = 29;
	private static final int MAX_DAY_IN_FEB_NON_LEAP_YEAR = 28;
	private static final int MAX_MONTH_IN_YEAR = 12;
	
	private final int DAY_OF_WEEK_START_POSITION = 0;
	private final int DAY_OF_WEEK_END_POSITION = 3;
	
	private static final ArrayList<Integer> LONG_MONTH_NUMBER = new ArrayList<Integer>(Arrays.asList(1,3,5,7,8,10,12));
	private static final int FEBURARY = 2;
	
	private static final int DATE_CLASS_YEAR_BASE = 1900;
	private static final int MONTH_OFFSET = 1;
	
	private static final int ONE = 1;
	private static final int ZERO = 0;
	private static final int TEN = 10;
	
	@SuppressWarnings("deprecation")
	public Time(int year, int month, int date, int hour, int minute){
		super(year, month-MONTH_OFFSET,date,hour,minute);
	}
	
	public Time(String timeString){
		this(getYearFromTimeString(timeString),getMonthFromTimeString(timeString),getDateFromTimeString(timeString),
				getHoursFromTimeString(timeString),getMinutesFromTimeString(timeString));
	}
	
	// Uses recurrence to generate a new Time object which is n days after this
	// Precondition: n has to be an integer, i.e., the minimum unit of time we can add is one day
	@SuppressWarnings("deprecation")
	public Time addDay(int n){

		int oldMinute = this.getMinutes();
		int oldHour = this.getHours();
		int oldDate = this.getDate();
		int oldMonth = this.getMonth();
		int oldYear = this.getYear();
		int maxDaysInCurrMonth = getMaxDaysInCurrMonth(oldMonth+MONTH_OFFSET,oldYear);

		if(n > (maxDaysInCurrMonth-oldDate)){
			int newMinute = oldMinute;
			int newHour = oldHour;
			int newDate = ONE;
			int newMonth;
			int newYear;

			if(oldMonth == MAX_MONTH_IN_YEAR){
				newMonth = ONE;
				newYear = oldYear+ONE;	
			}else{
				newMonth = oldMonth+ONE;
				newYear = oldYear;
			}

			Time temp = new Time(newYear,newMonth+MONTH_OFFSET,newDate,newHour,newMinute);
			return temp.addDay(n-maxDaysInCurrMonth+oldDate-ONE);
		}else{
			int newMinute = oldMinute;
			int newHour = oldHour;
			int newDate = oldDate+n;
			int newMonth = oldMonth;
			int newYear = oldYear;

			return new Time(newYear,newMonth+MONTH_OFFSET,newDate,newHour,newMinute);
		}
	}

	private int getMaxDaysInCurrMonth(int month, int year){
		assert(month > ZERO && month <= MAX_DAY_IN_LONG_MONTH);

		if(isFebrary(month)){
			if(isLeapYear(year)){
				return MAX_DAY_IN_FEB_LEAP_YEAR;
			}else{
				return MAX_DAY_IN_FEB_NON_LEAP_YEAR;
			}
		}else if(isLongMonth(month)){
			return MAX_DAY_IN_LONG_MONTH;
		}else{
			return MAX_DAY_IN_SHORT_MONTH;
		}
	}

	private boolean isLeapYear(int year){
		if(divisibleByFourHundred(year) 
			|| divisibleByFour(year)){
			return true;
		}else{
			return false;
		}
	}

	private boolean isFebrary(int month){
		return month == FEBURARY;
	}

	private boolean isLongMonth(int month){
		return LONG_MONTH_NUMBER.contains(month);
	}

	private boolean divisibleByFourHundred(int year){
		return isZero(year%400);
	}

	private boolean divisibleByFour(int year){
		return isZero(year%4);
	}

	private boolean isZero(int n){
		return n==ZERO;
	}
	
	private static int getYearFromTimeString(String timeString){
		assert(timeString.length() == TIME_STRING_LENGTH);
		return Integer.parseInt(timeString.substring(YEAR_STRING_START, YEAR_STRING_END));
	}
	
	private static int getMonthFromTimeString(String timeString){
		assert(timeString.length() == TIME_STRING_LENGTH);
		return Integer.parseInt(timeString.substring(MONTH_STRING_START,MONTH_STRING_END));
	}
	
	private static int getDateFromTimeString(String timeString){
		assert(timeString.length() == TIME_STRING_LENGTH);
		return Integer.parseInt(timeString.substring(DATE_STRING_START,DATE_STRING_END));
	}
	
	private static int getHoursFromTimeString(String timeString){
		assert(timeString.length() == TIME_STRING_LENGTH);
		return Integer.parseInt(timeString.substring(HOUR_STRING_START,HOUR_STRING_END));
	}
	
	private static int getMinutesFromTimeString(String timeString){
		assert(timeString.length() == TIME_STRING_LENGTH);
		return Integer.parseInt(timeString.substring(MIN_STRING_START, MIN_STRING_END));
	}

	@SuppressWarnings("deprecation")
	public int getThisYear(){
		return this.getYear() + DATE_CLASS_YEAR_BASE;
	}

	@SuppressWarnings("deprecation")
	public int getThisMonth(){
		return this.getMonth() + MONTH_OFFSET;
	}
	
	@SuppressWarnings("deprecation")
	public int getThisDate(){
		return this.getDate();
	}
	
	@SuppressWarnings("deprecation")
	public int getThisHours(){
		return this.getHours();
	}
	
	@SuppressWarnings("deprecation")
	public int getThisMinutes(){
		return this.getMinutes();
	}

	private String yearToString(int year){
		return Constant.EMPTY_STRING + year;
	}

	private String dateToString(int d){
		if(d<TEN){
			return Constant.EMPTY_STRING+ZERO_ADDON+d;
		}else{
			return Constant.EMPTY_STRING+d;
		}
	}

	private String monthToString(int m){
		m = m+MONTH_OFFSET;
		
		if(m<TEN){
			return Constant.EMPTY_STRING+ZERO_ADDON+m;
		}else{
			return Constant.EMPTY_STRING+m;
		}
	}

	private String hourToString(int hr){
		if(hr<TEN){
			return Constant.EMPTY_STRING+ZERO_ADDON+hr;
		}else{
			return Constant.EMPTY_STRING+hr;
		}
	}

	private String minToString(int min){
		if(min<TEN){
			return Constant.EMPTY_STRING+ZERO_ADDON+min;
		}else{
			return Constant.EMPTY_STRING+min;
		}
	}
	
	public int getMonToSun(){
		Date d = Time.timeToDate(this);
		String dayOfWeekAsString = d.toString().substring(DAY_OF_WEEK_START_POSITION,DAY_OF_WEEK_END_POSITION);
		int dayOfWeekAsInt;
		
		if(dayOfWeekAsString.equalsIgnoreCase(MONDAY)){
			dayOfWeekAsInt = MON_INT;
		}else if(dayOfWeekAsString.equalsIgnoreCase(TUESDAY)){
			dayOfWeekAsInt = TUE_INT;
		}else if(dayOfWeekAsString.equalsIgnoreCase(WEDNESDAY)){
			dayOfWeekAsInt = WED_INT;
		}else if(dayOfWeekAsString.equalsIgnoreCase(THURSDAY)){
			dayOfWeekAsInt = THUR_INT;
		}else if(dayOfWeekAsString.equalsIgnoreCase(FRIDAY)){
			dayOfWeekAsInt = FRI_INT;
		}else if(dayOfWeekAsString.equalsIgnoreCase(SATURDAY)){
			dayOfWeekAsInt = SAT_INT;
		}else if(dayOfWeekAsString.equalsIgnoreCase(SUNDAY)){
			dayOfWeekAsInt = SUN_INT;
		}else{			
			dayOfWeekAsInt = ZERO;
		}
		
		return dayOfWeekAsInt;
	}
	
	@Override
	public String toString(){
		String formattedTimeString = this.getFormattedTimeString();
		return formattedTimeString.replace(DATE_TIME_SEPARATOR,Constant.EMPTY_STRING);
	}

	@SuppressWarnings("deprecation")
	public String getFormattedTimeString(){
		int year = this.getYear();
		int month = this.getMonth();
		int date = this.getDate();
		int hour = this.getHours();
		int minute = this.getMinutes();

		String yearString = yearToString(year);
		String monthString = monthToString(month);
		String dateString = dateToString(date);
		String hourString = hourToString(hour);
		String minuteString = minToString(minute);

		return yearString+DATE_TIME_SEPARATOR+monthString+DATE_TIME_SEPARATOR+dateString+
				DATE_TIME_SEPARATOR+hourString+DATE_TIME_SEPARATOR+minuteString;
	}
	
	@SuppressWarnings("deprecation")
	public String getFormattedTimeStringSingaporeFormat(){
		int year = this.getYear();
		int month = this.getMonth();
		int date = this.getDate();
		int hour = this.getHours();
		int minute = this.getMinutes();

		String yearString = yearToString(year);
		String monthString = monthToString(month);
		String dateString = dateToString(date);
		String hourString = hourToString(hour);
		String minuteString = minToString(minute);

		return dateString+DATE_TIME_SEPARATOR+monthString+DATE_TIME_SEPARATOR+yearString+ 
				DATE_TIME_SEPARATOR+hourString+DATE_TIME_SEPARATOR+minuteString;
	}
	
	@SuppressWarnings("deprecation")
	public static Time dateToTime(Date date){
		int year = date.getYear() + DATE_CLASS_YEAR_BASE;
		int month = date.getMonth() + MONTH_OFFSET;
		int day = date.getDate();
		int hour = date.getHours();
		int minute = date.getMinutes();
		return new Time(year, month, day, hour, minute);
	}
	
	@SuppressWarnings("deprecation")
	public static Date timeToDate(Time t){
		int year = t.getThisYear() - DATE_CLASS_YEAR_BASE - DATE_CLASS_YEAR_BASE;
		int month = t.getThisMonth() - MONTH_OFFSET;
		int day = t.getThisDate();
		int hour = t.getThisHours();
		int minute = t.getThisMinutes();
		
		return new Date(year,month,day,hour,minute);
	}
	
	public static Time currentTime(){
		Date date =new Date();
		return dateToTime(date);		
	}
	
	/** @author A0105661M */
	public static Time convertInfoToTime(
			int[] dateInfo, int[] timeInfo, int startOrEnd) {
		//assert dateInfo and timeInfo are legal input
		int timeIndex = (startOrEnd-1) * 2;
		int dateIndex = (startOrEnd-1) * 3;
		
		int years = dateInfo[dateIndex];
		int months = dateInfo[dateIndex+1];
		int days = dateInfo[dateIndex+2];
		int hours = timeInfo[timeIndex];
		int minutes = timeInfo[timeIndex+1];
		
		Time timeResult
			= new Time(years, months, days, hours, minutes);
		
		return timeResult;
	}
}